
#include "apdu_lib_rsa_mgr.h"
#include "apdu_lib_common.h"

#include "../apdu.core/apdu.h"

#include "../../mk_lib/mk_auto_ptr.h"
#include "../../mk_lib/mk_utility.h"

#include <stdlib.h>
#include <string.h>
#include "../apdu.algo/mac.h"

extern int	g_sw;

int app_gen_rsa_keypair( HANDLE devHandle, int app_id, int container_id, int modulus_len,
						unsigned char * rsa_pub_key_blob, int blob_len )
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_gen_rsa_keypair(app_id, container_id, modulus_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}
	
	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(blob_len < data_len)
	{
		return 3;
	}

	memcpy(rsa_pub_key_blob, data, data_len);

	return 0;
}

int app_gen_rsa_keypair_ex(HANDLE devHandle, int app_id, int container_id, int modulus_len, int key_spec,
						   unsigned char * rsa_pub_key_blob, int blob_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_gen_rsa_keypair_ex(app_id, container_id, key_spec, modulus_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(blob_len < data_len)
	{
		return 3;
	}

	memcpy(rsa_pub_key_blob, data, data_len);

	return 0;
}

int app_import_rsa_keypair( HANDLE devHandle, int app_id, int container_id, unsigned long sym_alg_id, 
						   unsigned char * wrapped_key, int wrapped_key_len, int bits, 
						   unsigned char * encrypted_data, int encrypt_data_len )
{
	//transmit head part
	apdu * headApdu  =  NULL;
	headApdu = get_rsa_mgr()->create_apdu_import_rsa_keypair_head(app_id, container_id, sym_alg_id,
							wrapped_key_len, wrapped_key, bits, encrypt_data_len);
	headApdu->set_not_last_ins();

	mk_auto_ptr<apdu> ap(headApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,headApdu, &g_sw))
	{
		return 1;
	}
	if(g_sw != 0x9000)
	{
		return 2;
	}

	//transmit body part one by one
	int rv = 0;
	int max_transmit_len = get_max_transmit_len();
	int apdu_num = (encrypt_data_len + max_transmit_len - 1) / max_transmit_len;

	apdu * bodyApdu  = NULL;
	for(int i = 0; i < apdu_num ; i ++)
	{
		unsigned char * pBuff = encrypted_data + i * max_transmit_len;
		int len = encrypt_data_len  - i * max_transmit_len;

		if(len > max_transmit_len)
			len = max_transmit_len;

		bodyApdu = get_apdu_mgr()->create_apdu_transmit_more_data(headApdu, pBuff, len);

		if( i < apdu_num - 1)
			bodyApdu->set_not_last_ins();
		else
			bodyApdu->set_last_ins();

		if( 0 != get_dev_mgr()->transmit_apdu(devHandle, bodyApdu, &g_sw))
		{
			rv = 1;
			break;
		}

		if(g_sw != 0x9000)
		{
			rv = 2;
			break;
		}

		delete bodyApdu;
		bodyApdu = NULL;
	}

	if(bodyApdu)
	{
		delete bodyApdu;
	}
	
	return rv;
}

int app_rsa_sign_data( HANDLE devHandle, int app_id, int container_id,
					  int input_mode, int hash_algo, 
					  unsigned char * input_data, int data_len,
					  unsigned char * signature, int *sig_len )
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_sign_data(app_id, container_id, input_mode, hash_algo,
					input_data, data_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*sig_len < data_len)
	{
		return 3;
	}

	memcpy(signature, data, data_len);
	*sig_len = data_len;

	return 0;
}

int app_rsa_verify( HANDLE devHandle, int data_mode, int hash_algo, 
				   unsigned char * pub_key_blob, int key_blob_len, unsigned char * input_data, int data_len, 
				   unsigned char * signature, int sig_len )
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_verify(data_mode, hash_algo, pub_key_blob, key_blob_len,
		input_data, data_len, signature, sig_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	return 0;
}

int app_rsa_export_session_key( HANDLE devHandle, int app_id, int container_id, unsigned long alg_id,
							   unsigned char * pub_key_blob, int key_blob_len,
							   int * session_key_id, unsigned char * encrypt_session_key, int *session_key_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_export_session_key(app_id,container_id, alg_id, pub_key_blob,
		key_blob_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}
	
	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*session_key_len + 4 < data_len)
	{
		return 3;
	}

	*session_key_id = 0;
	memcpy(session_key_id, data, 2);
	mk_utility::reverse_bytes(session_key_id, 2);
	
	*session_key_len = data_len - 2;
	memcpy(encrypt_session_key, data + 2, data_len - 2);

	return 0;
}

int app_rsa_export_session_key_ex( HANDLE devHandle, int app_id, int container_id, int session_key_id,
								  unsigned char * pub_key_blob, int key_blob_len, 
								  unsigned char * encrypt_session_key, int * session_key_len )
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_export_session_key_ex(app_id,container_id, session_key_id, pub_key_blob,
						key_blob_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*session_key_len < data_len)
	{
		return 3;
	}

	*session_key_len = data_len;
	memcpy(encrypt_session_key, data, data_len);

	return 0;
}

int app_rsa_ext_rsa_pubkey_operation( HANDLE devHandle, unsigned char * pub_key_blob, int key_blob_len, 
									 unsigned char * input_data, int input_len, 
									 unsigned char * output_data, int * output_len )
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_ext_rsa_pubkey_operation(pub_key_blob,key_blob_len,
									input_data, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*output_len < data_len)
	{
		return 3;
	}
	*output_len = data_len;
	memcpy(output_data, data, data_len);

	return 0;
}

int app_rsa_ext_rsa_encrypt(HANDLE devHandle, unsigned char * pub_key_blob, int key_blob_len, 
							unsigned char * input_data, int input_len, 
							unsigned char * output_data, int * output_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_ext_rsa_encrypt(pub_key_blob,key_blob_len,
		input_data, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*output_len < data_len)
	{
		return 3;
	}
	*output_len = data_len;
	memcpy(output_data, data, data_len);

	return 0;
}



int app_rsa_public(HANDLE devHandle, int app_id, int container_id, int key_spec,
				   unsigned char * input_data, int input_len,
				   unsigned char * output_data, int *output_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_public(app_id, container_id, key_spec, input_data, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*output_len < data_len)
	{
		return 3;
	}
	
	*output_len = data_len;
	memcpy(output_data, data, data_len);

	return 0;
}

int app_rsa_private(HANDLE devHandle, int app_id, int container_id, int key_spec,
					unsigned char * input_data, int input_len,
					unsigned char * output_data, int * output_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_private(app_id, container_id, key_spec, input_data, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*output_len < data_len)
	{
		return 3;
	}

	*output_len = data_len;
	memcpy(output_data, data, data_len);

	return 0;
}

int app_rsa_encrypt_pkcs1(HANDLE devHandle, int app_id, int container_id, int key_spec,
						  unsigned char * plaint, int plaint_len,
						  unsigned char * cipher, int *cipher_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_encrypt(app_id, container_id, key_spec, plaint, plaint_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*cipher_len < data_len)
	{
		return 3;
	}
	
	*cipher_len = data_len;
	memcpy(cipher, data, data_len);

	return 0;
}

int app_rsa_decrypt_pkcs1(HANDLE devHandle, int app_id, int container_id, int key_spec,
						  unsigned char * cipher, int cipher_len,
						  unsigned char * plaint, int * plaint_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_decrypt(app_id, container_id, key_spec, cipher, cipher_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*plaint_len < data_len)
	{
		return 3;
	}

	*plaint_len = data_len;
	memcpy(plaint, data, data_len);

	return 0;
}

int app_rsa_sign_ex(HANDLE devHandle, int app_id, int container_id, int key_spec, int hash_algo,
					unsigned char * input,  int input_len, 
					unsigned char * signature, int *sig_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_sign_ex(app_id, container_id, key_spec, hash_algo, 
						input, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*sig_len < data_len)
	{
		return 3;
	}

	*sig_len = data_len;
	memcpy(signature, data, data_len);

	return 0;
}

int app_rsa_verify_ex(HANDLE devHandle, int app_id, int container_id, int key_spec,int hash_algo,
					  unsigned char * data,  int data_len, 
					  unsigned char * signature, int sig_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_verify_ex(app_id, container_id, key_spec, hash_algo,
							data, data_len, signature, sig_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	return 0;
}

int app_import_ext_rsa_keypair(HANDLE devHandle,int app_id, int container_id, int key_spec,
							   unsigned char * prikey_blob, int blob_len)
{
	//transmit head part
	apdu * headApdu  =  NULL;
	headApdu = get_rsa_mgr()->create_apdu_import_ext_rsa_keypair_head(app_id, container_id, key_spec);
	headApdu->set_not_last_ins();

	mk_auto_ptr<apdu> ap(headApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,headApdu, &g_sw))
	{
		return 1;
	}
	if(g_sw != 0x9000)
	{
		return 2;
	}

	//transmit body part one by one
	int rv = 0;
	int max_transmit_len = get_max_transmit_len();
	int apdu_num = (blob_len + max_transmit_len - 1) / max_transmit_len;

	apdu * bodyApdu  = NULL;
	for(int i = 0; i < apdu_num ; i ++)
	{
		unsigned char * pBuff = prikey_blob + i * max_transmit_len;
		int len = blob_len  - i * max_transmit_len;

		if(len > max_transmit_len)
			len = max_transmit_len;

		bodyApdu = get_apdu_mgr()->create_apdu_transmit_more_data(headApdu, pBuff, len);

		if( i < apdu_num - 1)
			bodyApdu->set_not_last_ins();
		else
			bodyApdu->set_last_ins();

		if( 0 != get_dev_mgr()->transmit_apdu(devHandle, bodyApdu, &g_sw))
		{
			rv = 1;
			break;
		}

		if(g_sw != 0x9000)
		{
			rv = 2;
			break;
		}

		delete bodyApdu;
		bodyApdu = NULL;
	}

	if(bodyApdu)
	{
		delete bodyApdu;
	}

	return rv;
}

int app_gen_ext_rsa_keypair( HANDLE devHandle, int bits, unsigned char * prikey_blob, int * blob_len )
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_gen_ext_rsa_keypair(bits);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*blob_len < data_len)
	{
		return 3;
	}

	*blob_len = data_len;
	memcpy(prikey_blob, data, data_len);

	return 0;
}

int app_export_ext_rsa_pubkey(HANDLE devHandle, unsigned char * pubkey_blob, int * blob_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_export_ext_rsa_pub_key();

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*blob_len < data_len)
	{
		return 3;
	}

	*blob_len = data_len;
	memcpy(pubkey_blob, data, data_len);

	return 0;
}

int app_import_ext_rsa_keypair_to_ram(HANDLE devHandle, int is_last, unsigned char * key_blob, int blob_len)
{
	apdu * newApdu  =  NULL;
	
	newApdu = get_rsa_mgr()->create_apdu_import_ext_rsa_keypair_to_ram(key_blob, blob_len);
	
	if(!is_last)
		newApdu->set_not_last_ins();

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	return 0;
}

int app_ram_rsa_public(HANDLE devHandle, unsigned char * input, int input_len, unsigned char * output, int * output_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_ram_rsa_operation(0, input, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*output_len < data_len)
	{
		return 3;
	}

	*output_len = data_len;
	memcpy(output, data, data_len);

	return 0;
}

int app_ram_rsa_private(HANDLE devHandle, unsigned char * input, int input_len, unsigned char * output, int * output_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_ram_rsa_operation(1, input, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*output_len < data_len)
	{
		return 3;
	}

	*output_len = data_len;
	if(output)
	{
		memcpy(output, data, data_len);
	}

	return 0;
}


int app_rsa_sign_data_interactive(HANDLE devHandle, int app_id, int container_id, int key_spec, int hash_algo,int timeout,
								  unsigned char * input,  int input_len, 
								  unsigned char * signature, int *sig_len)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_sign_interactive(app_id, container_id, key_spec, hash_algo, timeout,
		input, input_len);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	int data_len = 0;
	unsigned char * data = newApdu->get_response_data(&data_len);
	if(*sig_len < data_len)
	{
		return 3;
	}

	*sig_len = data_len;
	memcpy(signature, data, data_len);

	return 0;
}

int app_rsa_sign_data_interactive_cancel(HANDLE devHandle, int app_id, int container_id, int key_spec, int hash_algo)
{
	apdu * newApdu  =  NULL;
	newApdu = get_rsa_mgr()->create_apdu_rsa_sign_interactive_cancel(app_id, container_id, key_spec, hash_algo);

	mk_auto_ptr<apdu> ap(newApdu);

	if( 0 != get_dev_mgr()->transmit_apdu(devHandle,newApdu, &g_sw))
	{
		return 1;
	}

	if(g_sw != 0x9000)
	{
		return 2;
	}

	return 0;
}